//GNU C библиотека.
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h>


//Библиотека Qt.
#include <QTimer>
#include <QStringList>
#include <QFileInfo>


//Внутренняя библиотека.
#include "v4l2.h"
#include "vidstream.h"
#include "vidframe.h"


//===========================================================================//
//=Заголовок: V4L2::V4L2() - public
//=Параметры:
//=Описание: Конструктор.
//===========================================================================//
V4L2::V4L2(QString fname, int index, QObject *parent) : VidDevice(index, parent)
{
moveToThread(this);

devState = UNACTIVE; devFDesc = -1; devFName = fname;

v4l2PixFmt = V4L2_PIX_FMT_YUV420;
width = 512; height = 384;

maxNumBuffers = numBuffers = VIDEO_MAX_FRAME;

for(int i = 0; i < VIDEO_MAX_FRAME; ++i) frames[i] = NULL;

maxFramerate = 50; framerate = 25;

vidTimer = NULL;
}


//===========================================================================//
//=Заголовок: V4L2::~V4L2() - public
//=Параметры:
//=Описание: Деструктор.
//===========================================================================//
V4L2::~V4L2() {closeDevice();}


//===========================================================================//
//=Заголовок: bool V4L2::getListParam() - public
//=Параметры:
//=Описание: Функция возврата списка значений параметра устройства.
//===========================================================================//
bool V4L2::getListParam(DevParam::GET_LIST_PARAM param, QHash<int, QVariant> *valhash)
{
switch(param)
	{
	case DevParam::GET_LIST_INPUTS: enumInputs(valhash); break;
	case DevParam::GET_LIST_STANDARDS: enumStandards(valhash); break;
	case DevParam::GET_LIST_PIXFORMATS: enumPixFormats(valhash); break;
	case DevParam::GET_LIST_RESOLUTIONS: enumResolutions(valhash); break;
	default: return VidDevice::getListParam(param, valhash);
}

return true;
}


//===========================================================================//
//=Заголовок: bool V4L2::getParam() - public
//=Параметры:
//=Описание: Функция возврата параметра устройства.
//===========================================================================//
bool V4L2::getParam(DevParam::GET_PARAM param, QVariant *value)
{
switch(param)
	{
	case DevParam::GET_FILENAME: (*value) = devFName; break;
	case DevParam::GET_DEVNAME: (*value) = getDevName(); break;
	case DevParam::GET_DRIVERNAME: (*value) = getDriverName(); break;
	case DevParam::GET_INPUT: {
		int index;
		if(!getInput(&index)) return false;
		(*value) = index;
	} break;
	case DevParam::GET_STANDARD: {
		int index;
		if(!getStandard(&index)) return false;
		(*value) = index;
	} break;
	case DevParam::GET_PIXFORMAT: {
		int index;
		if(!getPixFormat(&index)) return false;
		(*value) = index;
	} break;
	case DevParam::GET_RESOLUTION: {
		int index;
		if(!getResolution(&index)) return false;
		(*value) = index;
	} break;
	case DevParam::GET_MAX_FRAMESCACHE: (*value) = maxNumBuffers; break;
	case DevParam::GET_FRAMESCACHE: (*value) = numBuffers; break;
	case DevParam::GET_MAX_FRAMERATE: (*value) = maxFramerate; break;
	case DevParam::GET_FRAMERATE: (*value) = framerate; break;
	case DevParam::GET_BRIGHTNESS: {
		int id = 0, min = 0, max = 0, step = 0, val = 0;
		if(!getLightRange(id, &min, &max, &step, &val)) return false;
		QHash<QString, QVariant> varhash;
		varhash.insert("min", min); varhash.insert("max", max);
		varhash.insert("step", step); varhash.insert("val", val);
		(*value) = varhash;
	} break;
	case DevParam::GET_CONTRAST: {
		int id = 1, min = 0, max = 0, step = 0, val = 0;
		if(!getLightRange(id, &min, &max, &step, &val)) return false;
		QHash<QString, QVariant> varhash;
		varhash.insert("min", min); varhash.insert("max", max);
		varhash.insert("step", step); varhash.insert("val", val);
		(*value) = varhash;
	} break;
	case DevParam::GET_HUE: {
		int id = 2, min = 0, max = 0, step = 0, val = 0;
		if(!getLightRange(id, &min, &max, &step, &val)) return false;
		QHash<QString, QVariant> varhash;
		varhash.insert("min", min); varhash.insert("max", max);
		varhash.insert("step", step); varhash.insert("val", val);
		(*value) = varhash;
	} break;
	case DevParam::GET_SATURATION: {
		int id = 3, min = 0, max = 0, step = 0, val = 0;
		if(!getLightRange(id, &min, &max, &step, &val)) return false;
		QHash<QString, QVariant> varhash;
		varhash.insert("min", min); varhash.insert("max", max);
		varhash.insert("step", step); varhash.insert("val", val);
		(*value) = varhash;
	} break;
	default: return VidDevice::getParam(param, value);
}

return true;
}


//===========================================================================//
//=Заголовок: void V4L2::slotSetParam() - public slots
//=Параметры:
//=Описание: Функция установки параметра устройства.
//===========================================================================//
void V4L2::slotSetParam(DevParam::SET_PARAM param, int value)
{
if(devState == UNACTIVE) {emit taskCompleted(VidDevice::getIndex()); return;}

//Сохранить текущее состояние устройства.
STATE devstate = devState;

//Определить, нужна ли остановка видеопотока
//для изменения параметра устройства.
bool need_restart = false;
switch(param)
	{
	case DevParam::SET_INPUT:
	case DevParam::SET_STANDARD:
	case DevParam::SET_PIXFORMAT:
	case DevParam::SET_RESOLUTION: need_restart = true; break;
	case DevParam::SET_FRAMESCACHE:
		if(maxNumBuffers >= value && value > 1)
			{numBuffers = value; need_restart = true;}
	break;
	default: break;
}

//Остановить видеопоток, если необходимо...
if(need_restart && devstate == CAPTURING) stopCapturing();

//Установить новое значение параметра устройства.
switch(param)
	{
	case DevParam::SET_INPUT:         setInput(value); break;
	case DevParam::SET_STANDARD:      setStandard(value); break;
	case DevParam::SET_PIXFORMAT:     setPixFormat(value); break;
	case DevParam::SET_RESOLUTION:    setResolution(value); break;
	case DevParam::SET_MAX_FRAMERATE: maxFramerate = value; break;
	case DevParam::SET_FRAMERATE:
		framerate = value;
		if(vidTimer) vidTimer->setInterval(1000 / framerate);
	break;
	case DevParam::SET_BRIGHTNESS: setLightValue(0, value); break;
	case DevParam::SET_CONTRAST:   setLightValue(1, value); break;
	case DevParam::SET_HUE:        setLightValue(2, value); break;
	case DevParam::SET_SATURATION: setLightValue(3, value); break;
	default: VidDevice::slotSetParam(param, value);
}

//Вернуть устройство к предыдущему состоянию.
if(need_restart && devstate == CAPTURING) startCapturing();

emit taskCompleted(VidDevice::getIndex());
}


//===========================================================================//
//=Заголовок: void V4L2::slotSetParam() - public slots
//=Параметры:
//=Описание: Функция установки параметра устройства.
//===========================================================================//
void V4L2::slotSetParam(DevParam::SET_PARAM param, bool value)
{
VidDevice::slotSetParam(param, value);
emit taskCompleted(VidDevice::getIndex());
}


//===========================================================================//
//=Заголовок: bool V4L2::openDevice() - protected
//=Параметры:
//=Описание: Функция открытия устройства.
//===========================================================================//
bool V4L2::openDevice() {return openDevice(devFName);}


//===========================================================================//
//=Заголовок: bool V4L2::openDevice() - protected
//=Параметры:
//=Описание: Функция открытия устройства.
//===========================================================================//
bool V4L2::openDevice(QString fname)
{
if(!fname.contains("/dev/video")) return false;

//Сохранить наименование файла устройства.
devFName = fname;

//Открыть файл устройства.
int devfdesc = open((const char*)fname.toAscii(), O_RDWR | O_NONBLOCK);
if(devfdesc == -1)
	{
	QString msg = tr("запрос на открытие файла устройства ") + fname + tr(" не выполнен");
	emit critical(msg); return false;
}

//Сохранить файловый дескриптор устройства.
devFDesc = devfdesc;

//Проверить совместимость устройства.
struct v4l2_capability capability;
memset(&capability, 0, sizeof(capability));
if(ioctl(devfdesc, VIDIOC_QUERYCAP, &capability) == -1)
	{
	QString msg = tr("файл устройства ") + fname + tr(" не является устройством V4L2");
	emit warning(msg); closeDevice(); return false;
}

//Установить видеоформат по умолчанию.
setPixFormat(v4l2PixFmt);

//Установить базовое имя устройства.
QFileInfo finfo(fname);
VidDevice::setBaseName(finfo.baseName());

//Создать видеотаймер.
if(!vidTimer)
	{
	vidTimer = new QTimer(this);
	vidTimer->setInterval(1000 / framerate);
	connect(vidTimer, SIGNAL(timeout()), this, SLOT(slotVidTimerTimeout()), Qt::DirectConnection);
}

//Установить состояние устройства.
devState = ACTIVE;

QString msg = tr("выполнен запрос на открытие файла устройства ") + fname;
emit information(msg);

return true;
}


//===========================================================================//
//=Заголовок: bool V4L2::closeDevice() - protected
//=Параметры:
//=Описание: Функция закрытия устройства.
//===========================================================================//
void V4L2::closeDevice()
{
//Остановить видеозахват.
if(devState == CAPTURING) stopCapturing();

//Закрыть устройство.
if(devFDesc != -1) close(devFDesc);

devState = UNACTIVE; devFDesc = -1;

QString msg = tr("выполнен запрос на закрытие устройства");
emit information(msg);
}


//===========================================================================//
//=Заголовок: bool V4L2::startCapturing() - protected
//=Параметры:
//=Описание: Функция запуска видеозахвата.
//===========================================================================//
bool V4L2::startCapturing()
{
//Указать устройству, что необходимо создание видеобуферов.
struct v4l2_requestbuffers requestbuffers;
memset(&requestbuffers, 0, sizeof(requestbuffers));
requestbuffers.count  = VIDEO_MAX_FRAME;
requestbuffers.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
requestbuffers.memory = V4L2_MEMORY_MMAP;
if(ioctl(devFDesc, VIDIOC_REQBUFS, &requestbuffers) == -1)
	{
	QString msg = tr("запрос VIDIOC_REQBUFS не выполнен");
	emit critical(msg); return false;
}

if(requestbuffers.count < 2)
	{
	QString msg = tr("Количество определённых к созданию видеобуферов менее необходимого");
	emit critical(msg); return false;
}

maxNumBuffers = requestbuffers.count;
if(numBuffers > maxNumBuffers)
	numBuffers = maxNumBuffers;
if(numBuffers < 2) numBuffers = 2;

//Создать требуемое кол-во видеобуферов.
for(int index = 0; index < numBuffers; ++index)
	{
	struct v4l2_buffer buffer;
	memset(&buffer, 0, sizeof(buffer));
	buffer.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buffer.memory = V4L2_MEMORY_MMAP;
	buffer.index  = index;
	if(ioctl(devFDesc, VIDIOC_QUERYBUF, &buffer) == -1)
		{
		QString msg = tr("запрос VIDIOC_QUERYBUF не выполнен");
		emit critical(msg); return false;
	}

	if(!memMap(buffer.index, buffer.length, buffer.m.offset))
		{
		QString msg = tr("запрос на создание видеобуфера не выполнен");
		emit critical(msg);
		memUnmap(); return false;
	}
}

//Указать устройству, что необходимо активировать заполнение видеобуферов.
for(qBufIndex = 0; qBufIndex < numBuffers; ++qBufIndex)
	{
	struct v4l2_buffer buffer;
	memset(&buffer, 0, sizeof(buffer));
	buffer.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buffer.memory = V4L2_MEMORY_MMAP;
	buffer.index  = qBufIndex;
	if(ioctl(devFDesc, VIDIOC_QBUF, &buffer) == -1)
		{
		QString msg = tr("запрос VIDIOC_QBUF не выполнен");
		emit critical(msg);
		memUnmap(); return false;
	}
}

//Активировать видеозахват.
enum v4l2_buf_type buf_type;
buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(devFDesc, VIDIOC_STREAMON, &buf_type) == -1)
	{
	QString msg = tr("запрос VIDIOC_STREAMON не выполнен");
	emit critical(msg);
	memUnmap(); return false;
}

qBufIndex = 0; dqBufIndex = 0;

//Активировать видеотаймер.
vidTimer->start();

//Установить состояние устройства.
devState = CAPTURING;

QString msg = tr("выполнен запрос на активацию устройства");
emit information(msg);
emit taskCompleted(VidDevice::getIndex());

return true;
}


//===========================================================================//
//=Заголовок: bool V4L2::stopCapturing() - protected
//=Параметры:
//=Описание: Функция остановки видеозахвата.
//===========================================================================//
bool V4L2::stopCapturing()
{
//Установить состояние устройства.
devState = ACTIVE;

//Остановить видеотаймер.
vidTimer->stop();

//Отправить событие об остановке видеопотока.
vidStream->sendFrame(NULL);

while(vidStream->isRunning()) {}

//Остановить видеопоток.
enum v4l2_buf_type buf_type;
buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(devFDesc, VIDIOC_STREAMOFF, &buf_type) == -1)
	{
	QString msg = tr("запрос VIDIOC_STREAMOFF не выполнен");
	emit critical(msg);
}

//Остановить детектор движения и запись в файл.
VidDevice::setMotionEnabled(false);

//Очистить видеопоток.
memUnmap();

QString msg = tr("выполнен запрос на остановку устройства");
emit information(msg);

return true;
}


//===========================================================================//
//=Заголовок: QString V4L2::getDevName() - private
//=Параметры:
//=Описание: Функция возврата наименования устройства.
//===========================================================================//
QString V4L2::getDevName()
{
struct v4l2_capability capability;
memset(&capability, 0, sizeof(capability));
if(ioctl(devFDesc, VIDIOC_QUERYCAP, &capability) == -1)
	{
	QString msg = tr("запрос на получение наименования устройства не выполнен");
	emit warning(msg); return QString();
}

//Получить наименование устройства.
char *name = new char[strlen((char*)capability.card) + 1];
strcpy(name, (char*)capability.card);
QString devname = QString(name);
delete name;

return devname;
}


//===========================================================================//
//=Заголовок: QString V4L2::getDriverName() - private
//=Параметры:
//=Описание: Функция возврата наименования драйвера устройства.
//===========================================================================//
QString V4L2::getDriverName()
{
struct v4l2_capability capability;
memset(&capability, 0, sizeof(capability));
if(ioctl(devFDesc, VIDIOC_QUERYCAP, &capability) == -1)
	{
	QString msg = tr("запрос на получение наименования драйвера устройства не выполнен");
	emit warning(msg); return QString();
}

//Получить наименование драйвера устройства.
char *name = new char[strlen((char*)capability.driver) + 1];
strcpy(name, (char*)capability.driver);
QString drivername = QString(name);
delete name;

return drivername;
}


//===========================================================================//
//=Заголовок: void V4L2::enumInputs() - private
//=Параметры:
//=Описание: Функция возврата списка видеовходов.
//===========================================================================//
void V4L2::enumInputs(QHash<int, QVariant> *inputshash)
{
v4l2_input input;
memset(&input, 0, sizeof(input));
input.index = 0;

while(ioctl(devFDesc, VIDIOC_ENUMINPUT, &input) != -1)
	{
	//Получить наименование видеовхода.
	char *name = new char[strlen((char*)input.name) + 1];
	strcpy(name, (char*)input.name);
	inputshash->insert(input.index, QString(name));
	delete name;
	input.index++;
}
}


//===========================================================================//
//=Заголовок: bool V4L2::setInput() - private
//=Параметры:
//=Описание: Функция установки видеовхода по индексу текущим.
//===========================================================================//
bool V4L2::setInput(int index)
{
if(ioctl(devFDesc, VIDIOC_S_INPUT, &index) == -1)
	{
	QString msg = tr("запрос на установку видеовхода не выполнен");
	emit warning(msg); return false;
}

QString msg = tr("выполнен запрос на установку видеовхода");
emit information(msg);

return true;
}


//===========================================================================//
//=Заголовок: bool V4L2::getInput() - private
//=Параметры:
//=Описание: Функция возврата индекса текущего видеовхода.
//===========================================================================//
bool V4L2::getInput(int *index)
{
if(ioctl(devFDesc, VIDIOC_G_INPUT, index) == -1)
	{
	QString msg = tr("запрос на получение индекса текущего видеовхода не выполнен");
	emit warning(msg); return false;
}

return true;
}


//===========================================================================//
//=Заголовок: void V4L2::enumStandards() - private
//=Параметры:
//=Описание: Функция возврата списка видеостандартов.
//===========================================================================//
void V4L2::enumStandards(QHash<int, QVariant> *standardshash)
{
//Получить индекс текущего видеовхода.
int index;
if(!getInput(&index)) return;

//Получить параметры текущего видеовхода.
struct v4l2_input input;
memset(&input, 0, sizeof(input));
input.index = index;
if(ioctl(devFDesc, VIDIOC_ENUMINPUT, &input) == -1)
	{
	QString msg = tr("запрос на получение параметров текущего видеовхода не выполнен");
	emit warning(msg); return;
}

//Получить наименования видеостандартов.
struct v4l2_standard standard;
memset(&standard, 0, sizeof(standard));
standard.index = 0;
while(ioctl(devFDesc, VIDIOC_ENUMSTD, &standard) == 0)
	{
	if(standard.id & input.std)
		{
		char *name = new char[strlen((char*)standard.name) + 1];
		strcpy(name, (char*)standard.name);
		standardshash->insert(standard.id, QString(name));
		delete name;
	}
	standard.index++;
}
}


//===========================================================================//
//=Заголовок: bool V4L2::setStandard() - private
//=Параметры:
//=Описание: Функция установки видеостандарта по индексу текущим.
//===========================================================================//
bool V4L2::setStandard(int index)
{
struct v4l2_standard standard;
memset(&standard, 0, sizeof(standard));
standard.index = 0;
while(ioctl(devFDesc, VIDIOC_ENUMSTD, &standard) == 0)
	{
	if(standard.id == (unsigned int)index)
		{
		if(ioctl(devFDesc, VIDIOC_S_STD, &standard.id) == -1)
			{
			QString msg = tr("запрос на установку видеостандарта не выполнен");
			emit warning(msg); return false;
		}
		QString msg = tr("выполнен запрос на установку видеостандарта");
		emit information(msg);
		return true;
	}
	standard.index++;
}

QString msg = tr("запрос на установку видеостандарта не выполнен");
emit warning(msg);
return false;
}


//===========================================================================//
//=Заголовок: bool V4L2::getStandard() - private
//=Параметры:
//=Описание: Функция возврата индекса текущего видеостандарта.
//===========================================================================//
bool V4L2::getStandard(int *index)
{
//Получить идентификационный номер текущего видеостандарта.
v4l2_std_id std_id;
if(ioctl(devFDesc, VIDIOC_G_STD, &std_id) == -1)
	{
	QString msg = tr("запрос на получение индекса текущего видеостандарта не выполнен");
	emit warning(msg); return false;
}

//Получить индекс текущего видеостандарта.
struct v4l2_standard standard;
memset(&standard, 0, sizeof(standard));
standard.index = 0;
while(ioctl(devFDesc, VIDIOC_ENUMSTD, &standard) == 0)
	{
	if(standard.id & std_id) {(*index) = standard.id; return true;}
	standard.index++;
}

QString msg = tr("запрос на получение текущего видеостандарта не выполнен");
emit warning(msg);
return false;
}


//===========================================================================//
//=Заголовок: void V4L2::enumPixFormats() - private
//=Параметры:
//=Описание: Функция возврата списка видеоформатов.
//===========================================================================//
void V4L2::enumPixFormats(QHash<int, QVariant> *formatshash)
{
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

while(ioctl(devFDesc, VIDIOC_ENUM_FMT, &fmtdesc) == 0)
	{
	char *name = new char[strlen((char*)fmtdesc.description) + 1];
	strcpy(name, (char*)fmtdesc.description);
	formatshash->insert(fmtdesc.pixelformat, QString(name));
	delete name;
	fmtdesc.index++;
}
}


//===========================================================================//
//=Заголовок: bool V4L2::setPixFormat() - private
//=Параметры:
//=Описание: Функция установки видеоформата.
//===========================================================================//
bool V4L2::setPixFormat(int index)
{
//Получить описание вновь устанавливаемого видеоформата.
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while(ioctl(devFDesc, VIDIOC_ENUM_FMT, &fmtdesc) == 0)
	{
	if(fmtdesc.pixelformat == (unsigned int)index) break;
	fmtdesc.index++;
}

//Установить видеоформат.
struct v4l2_format format;
memset(&format, 0, sizeof(format));
format.type                 = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width        = width;
format.fmt.pix.height       = height;
format.fmt.pix.field        = V4L2_FIELD_INTERLACED;
format.fmt.pix.pixelformat  = fmtdesc.pixelformat;
if(ioctl(devFDesc, VIDIOC_S_FMT, &format) == -1) return false;

v4l2PixFmt = format.fmt.pix.pixelformat;

QString msg = tr("выполнен запрос на установку видеоформата");
emit information(msg);

return true;
}


//===========================================================================//
//=Заголовок: bool V4L2::getPixFormat() - private
//=Параметры:
//=Описание: Функция возврата индекса текущего видеоформата.
//===========================================================================//
bool V4L2::getPixFormat(int *index)
{
//Получить текущий установленный видеоформат.
struct v4l2_format format;
memset(&format, 0, sizeof(format));
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(devFDesc, VIDIOC_G_FMT, &format) == -1) return false;

//Получить индекс текущего установленного видеоформата.
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while(ioctl(devFDesc, VIDIOC_ENUM_FMT, &fmtdesc) == 0)
	{
	if(format.fmt.pix.pixelformat == fmtdesc.pixelformat)
		{
		(*index) = format.fmt.pix.pixelformat;
		v4l2PixFmt = format.fmt.pix.pixelformat;
		return true;
	}
	fmtdesc.index++;
}

return false;
}


//===========================================================================//
//=Заголовок: void V4L2::enumResolutions() - private
//=Параметры:
//=Описание: Функция возврата списка видеоразрешений.
//===========================================================================//
void V4L2::enumResolutions(QHash<int, QVariant> *reshash)
{
//Получить информацию о минимальной и максимальной градации видеоразрешения.
struct video_capability capability;
memset(&capability, 0, sizeof(capability));
if(ioctl(devFDesc, VIDIOCGCAP, &capability) == -1) return;

//Получить список поддерживаемых видеоустройствм вариантов видеоразрешений.
int min_w, min_h;
if(capability.minwidth < 256) min_w = 256; else min_w = capability.minwidth;
if(capability.minheight < 192) min_h = 192; else min_h = capability.minheight;
int koef_w = 64, koef_h = 48;
int index = 0;
while(1)
	{
	int w = min_w + koef_w;
	int h = min_h + koef_h;
	if(w > capability.maxwidth && h > capability.maxheight) {index++; break;}
	QString strresolution;
	if(w > capability.maxwidth)
		{
		strresolution = QString::number(w - 64) + QString("x") + QString::number(h);
		reshash->insert(index, strresolution);
		koef_h = koef_h + 48;
		index++; continue;
	}
	if(h > capability.maxheight)
		{
		strresolution = QString::number(w) + QString("x") + QString::number(h - 48);
		reshash->insert(index, strresolution);
		koef_w = koef_w + 64;
		index++; continue;
	}
	reshash->insert(index, QString::number(w) + QString("x") + QString::number(h));
	koef_w = koef_w + 64; koef_h = koef_h + 48;
	index++;
}

//Добавить в список максимальное видеоразрешение.
QString strresolution = QString::number(capability.maxwidth) + QString("x") + QString::number(capability.maxheight);
reshash->insert(index, strresolution);
}


//===========================================================================//
//=Заголовок: bool V4L2::setResolution() - private
//=Параметры:
//=Описание: Функция установки видеоразрешения.
//===========================================================================//
bool V4L2::setResolution(int index)
{
QHash<int, QVariant> reshash;
enumResolutions(&reshash);
QString name = reshash.value(index).toString();
QStringList reslist = name.split("x", QString::SkipEmptyParts);
if(reslist.size() != 2) return false;
bool ok = false;
int w = reslist.at(0).toInt(&ok);
if(!ok) return false;
ok = false;
int h = reslist.at(1).toInt(&ok);
if(!ok) return false;

//Получить описание вновь устанавливаемого видеоформата.
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while(ioctl(devFDesc, VIDIOC_ENUM_FMT, &fmtdesc) == 0)
	{
	if(fmtdesc.pixelformat == (unsigned int)v4l2PixFmt) break;
	fmtdesc.index++;
}

//Установить видеоформат.
struct v4l2_format format;
memset(&format, 0, sizeof(format));
format.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width       = w;
format.fmt.pix.height      = h;
format.fmt.pix.pixelformat = fmtdesc.pixelformat;
format.fmt.pix.field       = V4L2_FIELD_INTERLACED;
if(ioctl(devFDesc, VIDIOC_S_FMT, &format) == -1) return false;

width = w; height = h;

QString msg = tr("выполнен запрос на установку видеоразрешения");
emit information(msg);

return true;
}


//===========================================================================//
//=Заголовок: bool V4L2::getResolution() - private
//=Параметры:
//=Описание: Функция возврата видеоразрешения.
//===========================================================================//
bool V4L2::getResolution(int *index)
{
//Получить текущий установленный видеоформат.
struct v4l2_format format;
memset(&format, 0, sizeof(format));
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(devFDesc, VIDIOC_G_FMT, &format) == -1) return false;

//Получить индекс текущего установленного видеоформата.
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while(ioctl(devFDesc, VIDIOC_ENUM_FMT, &fmtdesc) == 0)
	{
	if(format.fmt.pix.pixelformat == fmtdesc.pixelformat)
		{
		QString resstr = QString::number(format.fmt.pix.width);
		resstr += QString("x");
		resstr += QString::number(format.fmt.pix.height);

		QHash<int, QVariant> reshash;
		enumResolutions(&reshash);
		(*index) = reshash.key(resstr);

		width = format.fmt.pix.width;
		height = format.fmt.pix.height;
		return true;
	}
	fmtdesc.index++;
}

return false;
}


//===========================================================================//
//=Заголовок: bool V4L2::getLightRange() - private
//=Параметры:
//=Описание: Функция возврата информации о градации
//=          яркостной и/или цветовой настройки.
//===========================================================================//
bool V4L2::getLightRange(int id, int *min, int *max, int *step, int *value)
{
__u32 ctrlid;
switch(id)
	{
	case 0: ctrlid = V4L2_CID_BRIGHTNESS; break;
	case 1: ctrlid = V4L2_CID_CONTRAST; break;
	case 2: ctrlid = V4L2_CID_HUE; break;
	case 3: ctrlid = V4L2_CID_SATURATION; break;
	default: ctrlid = V4L2_CID_BRIGHTNESS;
}

struct v4l2_queryctrl queryctrl;
memset(&queryctrl, 0, sizeof(queryctrl));
queryctrl.id = ctrlid;
if(ioctl(devFDesc, VIDIOC_QUERYCTRL, &queryctrl) == -1) return false;
(*min) = queryctrl.minimum;
(*max) = queryctrl.maximum;
(*step) = queryctrl.step;

struct v4l2_control control;
memset(&control, 0, sizeof(control));
control.id = ctrlid;
if(ioctl(devFDesc, VIDIOC_G_CTRL, &control) == -1) return false;
(*value) = control.value;

return true;
}


//===========================================================================//
//=Заголовок: bool V4L2::setLightValue() - private
//=Параметры:
//=Описание: Функция установки нового значения
//=          яркостной и/или цветовой настройки.
//===========================================================================//
bool V4L2::setLightValue(int id, int value)
{
__u32 ctrlid;
switch(id)
	{
	case 0: ctrlid = V4L2_CID_BRIGHTNESS; break;
	case 1: ctrlid = V4L2_CID_CONTRAST; break;
	case 2: ctrlid = V4L2_CID_HUE; break;
	case 3: ctrlid = V4L2_CID_SATURATION; break;
	default: ctrlid = V4L2_CID_BRIGHTNESS;
}

struct v4l2_control control;
memset(&control, 0, sizeof(control));
control.id = ctrlid;
control.value = value;
if(ioctl(devFDesc, VIDIOC_S_CTRL, &control) == -1) return false;
return true;
}


//===========================================================================//
//=Заголовок: bool V4L2::memMap() - private
//=Параметры:
//=Описание: Функция выделения памяти под видеобуфер.
//===========================================================================//
bool V4L2::memMap(int index, int length, int offset)
{
uchar *vdata = (uchar *)mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, devFDesc, offset);
if(vdata == MAP_FAILED) return false;

VidFrame *vidframe = new VidFrame();
if(!vidframe) return false;
vidframe->setV4L2PixFormat(v4l2PixFmt);
vidframe->setResolution(QSize(width, height));
vidframe->setBuffer(vdata, length);

frames[index] = vidframe;

return true;
}


//===========================================================================//
//=Заголовок: void V4L2::memUnmap() - private
//=Параметры:
//=Описание: Функция освобождения памяти.
//===========================================================================//
void V4L2::memUnmap()
{
for(int i = 0; i < VIDEO_MAX_FRAME; ++i)
	{
	VidFrame *vidframe = frames[i];
	if(!vidframe) continue;
	munmap(vidframe->getData(), vidframe->getLength());
	delete vidframe;
	frames[i] = NULL;
}
}


//===========================================================================//
//=Заголовок: void V4L2::slotVidTimerTimeout() - private slots
//=Параметры:
//=Описание: Слот сработки видеотаймера.
//===========================================================================//
void V4L2::slotVidTimerTimeout()
{
v4l2_buffer buffer;
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer.memory = V4L2_MEMORY_MMAP;

if(!vidStream->isRunning())
	{
	//Отправить запрос на возврат видеобуфера для обработки.
	buffer.index = dqBufIndex;
	if(ioctl(devFDesc, VIDIOC_DQBUF, &buffer) == 0)
		{
		VidFrame *vidframe = frames[dqBufIndex];
		vidframe->deinterlace();
		vidStream->sendFrame(vidframe);

		//Вычислить следующий индекс видеобуфера для обработки.
		dqBufIndex++;
		if(dqBufIndex == numBuffers) dqBufIndex = 0;
	}
	else {
		if(errno == EAGAIN) {if(framerate > 1) framerate--;}
		else {
			QString msg = tr("запрос на возврат видеобуфера для обработки не выполнен");
			emit critical(msg);

			//Выполнить остановку видеопотока.
			stopCapturing();
			return;
		}
	}
}
else if(framerate > 1) framerate--;

//Исключить пересечение индексов видеобуферов для заполнения и обработки.
if(qBufIndex != dqBufIndex)
	{
	//Отправить запрос на заполнение видеобуфера.
	buffer.index = qBufIndex;
	if(ioctl(devFDesc, VIDIOC_QBUF, &buffer) == 0)
		{
		//Вычислить следующий индекс видеобуфера для заполнения.
		qBufIndex++;
		if(qBufIndex == numBuffers) qBufIndex = 0;
	}
	else {
		QString msg = tr("запрос на заполнение видеобуфера не выполнен");
		emit critical(msg);

		//Выполнить остановку видеопотока.
		stopCapturing();
		return;
	}
}

//Установить кадровый период.
if(framerateTime.isValid())
	{
	if(framerateTime.elapsed() >= 1000)
		{
		vidTimer->setInterval(1000 / framerate);
		emit framerateChanged(framerate);
		framerateTime.restart();

		if(framerate < maxFramerate) framerate++;
		else if(framerate > 1) framerate--;
	}
}
else framerateTime.start();

}
